#
#    Copyright (c) 2024 Project CHIP Authors
#    All rights reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License");
#    you may not use this file except in compliance with the License.
#    You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.
#

# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments
# for details about the block below.
#
# === BEGIN CI TEST ARGUMENTS ===
# test-runner-runs:
#   run1:
#     app: ${ALL_CLUSTERS_APP}
#     app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json
#     script-args: >
#       --storage-path admin_storage.json
#       --commissioning-method on-network
#       --discriminator 1234
#       --passcode 20202021
#       --PICS src/app/tests/suites/certification/ci-pics-values
#       --trace-to json:${TRACE_TEST_JSON}.json
#       --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
#     factory-reset: true
#     quiet: true
# === END CI TEST ARGUMENTS ===


from mobly import asserts

import matter.clusters as Clusters
from matter.exceptions import ChipStackError
from matter.testing.matter_testing import MatterBaseTest, async_test_body, default_matter_test_main


class TestRevokeCommissioningClearsPASE(MatterBaseTest):

    @async_test_body
    async def test_TestRevokeCommissioningClearsPASE(self):

        self.print_step("precondition", "Commissioning - already done")

        self.TH1 = self.default_controller

        self.print_step("precondition", "Create Second Controller")

        fabric_admin = self.certificate_authority_manager.activeCaList[0].adminList[0]
        self.TH2_nodeid = self.matter_test_config.controller_node_id + 1
        self.TH2 = fabric_admin.NewController(
            nodeId=self.TH2_nodeid,
            paaTrustStorePath=str(self.matter_test_config.paa_trust_store_path),
        )

        self.print_step(1, "TH1 sends an OpenCommissioningWindow command to DUT to allow it to be commissioned by TH2")
        resp = await self.open_commissioning_window()

        self.print_step(2, "TH2 establishes a PASE session with DUT")
        pase_node_id = self.dut_node_id + 1
        await self.TH2.FindOrEstablishPASESession(setupCode=resp.commissioningParameters.setupQRCode, nodeId=pase_node_id)

        self.print_step(3, "Read VendorName from BasicInformation Cluster using TH2 over PASE, to ensure PASE session is established")

        VendorNameAttr = Clusters.BasicInformation.Attributes.VendorName
        ROOT_NODE_ENDPOINT_ID = 0

        read_step3 = await self.TH2.ReadAttribute(
            nodeId=pase_node_id,
            attributes=(ROOT_NODE_ENDPOINT_ID, VendorNameAttr),
        )

        asserts.assert_in(
            VendorNameAttr,
            read_step3[ROOT_NODE_ENDPOINT_ID][Clusters.BasicInformation],
            "VendorName should be present in the read response"
        )

        self.print_step(4, "TH1 Sends RevokeCommissioning (over CASE) to clear PASE session on DUT")
        revokeCmd = Clusters.AdministratorCommissioning.Commands.RevokeCommissioning()
        await self.TH1.SendCommand(nodeId=self.dut_node_id, endpoint=0, payload=revokeCmd, timedRequestTimeoutMs=6000)

        self.print_step(
            5, "Ensure that the PASE Session got cleared, by attempting to read VendorName using TH2 (over PASE) and Ensuring that the Command times out")

        _CHIP_TIMEOUT_ERROR = 50

        with asserts.assert_raises(ChipStackError) as e:
            await self.TH2.ReadAttribute(
                nodeId=pase_node_id,
                attributes=(ROOT_NODE_ENDPOINT_ID, VendorNameAttr))
        asserts.assert_equal(e.exception.err, _CHIP_TIMEOUT_ERROR,
                             f"Expected timeout error reading VendorName attribute over PASE, got {e.exception.err}")

        # ---------------------------- Repeat test, but sending RevokeCommissioning over PASE this time --------------------------------

        self.print_step(
            6, "recreate Second Controller; to establish a new PASE session and repeat test, but sending RevokeCommissioning over PASE this time")

        self.TH2.Shutdown()
        fabric_admin = self.certificate_authority_manager.activeCaList[0].adminList[0]
        self.TH2_nodeId = self.matter_test_config.controller_node_id + 1
        self.TH2 = fabric_admin.NewController(
            nodeId=self.TH2_nodeId,
            paaTrustStorePath=str(self.matter_test_config.paa_trust_store_path),
        )

        self.print_step(7, "TH1 sends an OpenCommissioningWindow command to DUT to allow it to be commissioned by TH2")
        resp = await self.open_commissioning_window()

        self.print_step(8, "TH2 establishes a PASE session with DUT")
        await self.TH2.FindOrEstablishPASESession(setupCode=resp.commissioningParameters.setupQRCode, nodeId=pase_node_id)

        self.print_step(9, "TH2 Sends RevokeCommissioning (Over PASE) to clear PASE session on DUT")

        await self.TH2.SendCommand(nodeId=pase_node_id, endpoint=0, payload=revokeCmd, timedRequestTimeoutMs=6000)

        self.print_step(
            10, "Ensure that the PASE Session got cleared, by attempting to read VendorName using TH2 (over PASE) and Ensuring that the Command times out")

        with asserts.assert_raises(ChipStackError) as e:
            await self.TH2.ReadAttribute(
                nodeId=pase_node_id,
                attributes=(ROOT_NODE_ENDPOINT_ID, VendorNameAttr))
        asserts.assert_equal(e.exception.err, _CHIP_TIMEOUT_ERROR,
                             f"Expected timeout error reading VendorName attribute over PASE, got {e.exception.err}")


if __name__ == "__main__":
    default_matter_test_main()
